<?php
/**
 * @name CXmlStore
 * @version 1.0
 * @todo : store format XML to file
 * @package lib
 * @subpackage data
*/
class CXmlStore extends CStore
{
	/**
	 * Lớp chứa các hàm thao tác với bộ lưu trữ bằng XML
	 *
	 */
	public $xml;
	public $file_name;
	public $root;
	public $xpath;
	public $loaded = false;
	/**
	 * Phương thức khởi tạo một đối tượng lưu trữ xml
	 *
	 * @param $file_name string Tên file
	 * @param $root_query mixed Tên của nút gốc
	 */
	function __construct($file_name, $root_query = '')
	{
		$this->file_name = $file_name;
		$this->root = $root_query;
		parent::__construct();
	}
	/**
	 * Phương thức mở file để chuẩn bị thực hiện các thao tác
	 *
	 */
	function connect()
	{
		$this->xml = new CXml();

	}
	/**
	 * Phương thức ghi thay đổi vào file
	 *
	 */
	function save()
	{
		if($this->file_name)
		{
			$this->xml->save($this->file_name);
		}
	}
	function load()
	{
		if(!$this->loaded and $this->file_name)
		{
			$this->xml->load($this->file_name);
			$this->loaded = true;
		}
	}
	function count($table, $conditions = false)
	{
		$this->load();
		return sizeof($this->select_all($table, $conditions));
	}
	/**
	 * Lấy một record từ XMLStore
	 *
	 * @param array $conditions
	 * @param array $options
	 * @return array
	 */
	function select($table, $conditions = false, $options = array())
	{
		$this->load();
		$xpath = new DOMXPath($this->xml->doc);
		if($items = $xpath->query($this->root.$table.$this->condition_string($conditions)))
		{
			if($items->length > 0)
			{
				return $this->xml->decode($items->item(0));
			}
		}
		return false;
	}

	function select_parent($table, $conditions = false)
	{
		$this->load();
		$xpath = new DOMXPath($this->xml->doc);
		if($items = $xpath->query($this->root.$table.$this->condition_string($conditions)))
		{
			if($items->length == 1)
			{
				return $this->xml->decode($items->item(0)->parentNode->parentNode);
			}
		}
		return false;
	}

	/**
	 * Lấy tất cả các record theo điều kiện từ XMLStore
	 *
	 * @param string $table
	 * @param array $conditions
	 * @param array $options
	 * @return array
	 */
	function select_all($table, $conditions = false, $options = array())
	{
		$this->load();
		if($table === false)
		{
			return $this->xml->decode();
		}
		else
		{
			$options = array_merge(array(
				'page_no' => 1,
				'items_per_page' => 100
			),$options);
			$xpath = new DOMXPath($this->xml->doc);
			$query_string = $this->root.$table.$this->condition_string($conditions);
	//		echo rtrim($query_string,']');
			if(!empty($conditions)){
				$query_string = rtrim($query_string,']').' and position()>'.($options['page_no']-1)*$options['items_per_page'].' and position()<='.$options['page_no']*$options['items_per_page'].']';
			}else{
				$query_string =$query_string.'[position()>'.($options['page_no']-1)*$options['items_per_page'].' and position()<='.$options['page_no']*$options['items_per_page'].']';
			}
			if($nodes = $xpath->query($query_string))
			{
				$items = array();
				foreach($nodes as $node)
				{
					$item = $this->xml->decode($node);
					if(is_array($item) and isset($item['id'])){
						$items[$item['id']] = $item;
					}else {
						$items[] = $item;
					}
				}
				if(isset($options['order_by']))
				{
					return CList($items)->order($options['order_by'])->items;
				}
				return $items;
			}
		}
		return false;
	}

	/**
	 * Thêm mới một record vào XML Store
	 *
	 * @param string $table
	 * @param array $values
	 * @return  bool
	 */
	function insert_list($table, $values)
	{
		if($values)
		{
			foreach($values as $value)
			{
				$this->insert($table, $value, false);
			}
			$this->save();
		}
	}
	function insert($table, $values, $save = true)
	{
		$this->load();
		$xpath = new DOMXPath($this->xml->doc);
		if($nodes = $xpath->query($this->root.$table) and $nodes->length > 0)
		{
//			file_put_contents('log.txt','new insert ==> insert tag (field) and values');
			$table_name = explode('/', $table);
			$nodes->item(0)->parentNode->appendChild($this->xml->create_element($table_name[count(($table_name))-1], $values));
			if($save)
			{
				$this->save();
			}
			return true;
		}
		else
		{
			$arr_table = explode('/',$table);
			$last_tag = $arr_table[count($arr_table)-1];
			$first_tag = $arr_table[count($arr_table)-2];
			array_pop($arr_table);
			//echo $this->root.'/'.implode('/',$arr_table);
			if($node = $xpath->query($this->root.implode('/',$arr_table)) and $node->length > 0){
				$data = array($last_tag=>$values);
				$this->xml->encode($data, $node->item(0));
				if($save)
				{
					$this->save();
				}
			}else {
				array_pop($arr_table);
				$path_way = implode('/',$arr_table);
				$data = array($first_tag=>array($last_tag=>$values));
				$parent_path = $this->root.$path_way;
				if($parent_path{strlen($parent_path)-1} == '/')
				{
					$parent_path = substr($parent_path, 0, strlen($parent_path)-1);
				}
				//echo $parent_path;
				if($node = $xpath->query($parent_path) and $node->length > 0){
					$this->xml->encode($data, $node->item(0));
					if($save)
					{
						$this->save();
					}
				}
				else
				{
					return false;
				}
			}

		}
	}

	/**
	 * Cập nhật một record của XML Store
	 *
	 * @param string $table
	 * @param array $values
	 * @param array $condition
	 */
	function update($table, $values, $condition)
	{
		$this->load();
		$xpath = new DOMXPath($this->xml->doc);
		if($nodes = $xpath->query($this->root.$table.$this->condition_string($condition)) and $nodes->length > 0)
		{
			$has_changed = false;
			foreach($nodes as $node)
			{
				if($node->parentNode)
				{
					$old_values = $this->xml->decode($node);
					$change = false;
					foreach($values as $name => $value)
					{
						if(!isset($old_values[$name]) or $old_values[$name] != $value)
						{
							$change = true;
						}
						$old_values[$name] = $value;
					}
					if($change)
					{
						$has_changed = true;
						$table_name = explode('/', $table);
						$node->parentNode->replaceChild($this->xml->create_element($table_name[count(($table_name))-1],$old_values), $node);
					}
				}
			}
			if($has_changed)
			{
				$this->save();
			}
		}
	}

	/**
	 * Xóa record theo điều kiện
	 *
	 * @param string $table
	 * @param array $condition
	 */
	function delete($table, $condition)
	{
		$this->load();
		$xpath = new DOMXPath($this->xml->doc);
		if($nodes = $xpath->query($this->root.$table.$this->condition_string($condition)) and $nodes->length > 0)
		{
			foreach($nodes as $node)
			{
				$node->parentNode->removeChild($node);
			}
		}
		$this->save();
	}
	/**
	 * Phương thức lấy ra tất cả các bản ghi của bảng (có sắp xếp
	 * @param string $table Tên bảng
	 * @param mixed $condition Điều kiện chọn
	 * @param string $field Trường dùng để sắp xếp
	 * @return Một mảng chứa tất cả các bản ghi của file
	 */
	function select_all_order($table, $conditions = false, $field)
	{
		$this->load();
		$xpath = new DOMXPath($this->xml->doc);
		if($nodes = $xpath->query($this->root.$table.$this->condition_string($conditions)))
		{
			$items = array();
			foreach($nodes as $node)
			{
				$items[] = $this->xml->decode($node);
			}
			$array_sort = array();
			foreach ($items as $value){
				$array_sort[] = $value[$field];
			}

			array_multisort($array_sort,$items);
			return $items;
		}
		return false;
	}

	/**
	 * Đếm số kết quả tìm được
	 *
	 * @param string $table
	 * @param mixed $conditions
	 * @param array $options
	 */
	function count_result($table, $conditions = false, $options = array()){
		$result = $this->select_all($table, $conditions, $options);
		if($result){
			return count($result);
		}else {
			return 0;
		}
	}

	static function convert_conditions($operator, $factors)
	{
		switch ($operator) {
			case 'COLUMN':
				return './'.addslashes($factors[0]);
				break;
			case 'FUNCTION':
				$params = $factors;
				unset($params[0]);
				return addslashes($factors[0]).'('.join(',', $params).')';
				break;
			case 'STRING':
			case 'INT':
			case 'REAL':
				return '\''.addslashes($factors[0]).'\'';
				break;
			case 'ID':
				return '(./id = '.$factors[0].')';
				break;
			case '!':
				return ' not ('.$factors[0].')';
				break;
			case 'IN':
				$sub_factors = array();
				for($i = 1; $i< sizeof($factors); $i++)
				{
					$sub_factors[] = '('.$factors[0].' = '.$factors[$i].' )';
				}
				return join(' or ', $sub_factors);
				break;
			default:
				if(sizeof($factors) == 1)
				{
					return strtolower($operator).'('.$factors[0].')';
				}
				else
				{
					return '('.join(strtolower($operator), $factors).')';
				}
				break;
		}
	}
	function condition_string($conditions)
	{
		if(is_string($conditions) or is_numeric($conditions))
		{
			$conditions = EId($conditions);
		}
		else
		{
			$conditions = CExpression::pre_process_conditions($conditions);
		}
		if($conditions)
		{
			return '['.$conditions->parse('CXmlStore::convert_conditions').']';
		}
		return false;
	}
}
/**
 * Tạo đối tượng của lớp CXmlStore
 * @param $file_name string Tên file
 * @param $root_query string Tên của nút gốc
 * @return Đối tượng CXmlStore
 */
function CXmlStore($file_name, $root_query = '')
{
	return new CXmlStore($file_name, $root_query);
}
?>